package com.hpsk.webSocket.controller;


import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import cn.zhxu.bs.util.MapBuilder;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hpsk.common.CommonException;
import com.hpsk.shopsatoken.model.user.service.IUserService;
import com.hpsk.shopsatoken.model.user.vo.UserVo;
import com.hpsk.webSocket.configurer.WebSocketConfig;
import com.hpsk.webSocket.constants.MsgCode;
import com.hpsk.webSocket.entity.Message;
import com.hpsk.webSocket.entity.SendMessage;
import com.hpsk.webSocket.enums.SendEnum;
import com.hpsk.webSocket.model.ai.dto.BizAiMessageContentParamDto;
import com.hpsk.webSocket.model.ai.dto.BizAiMessageParamDto;
import com.hpsk.webSocket.model.ai.entity.BizAiMessage;
import com.hpsk.webSocket.model.ai.entity.BizAiMessageContent;
import com.hpsk.webSocket.model.ai.service.IBizAiMessageContentService;
import com.hpsk.webSocket.model.ai.service.IBizAiMessageService;
import com.hpsk.webSocket.response.ServerResponseEntity;
import com.hpsk.webSocket.utils.AiUtil;
import jakarta.annotation.PostConstruct;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author huangwang2832511019
 */
@ConditionalOnClass(value = WebSocketConfig.class)
@ServerEndpoint("/im/websocket/info/{token}/{userId}")
@Component
@Slf4j
public class TestWebSocketController {
    //    @Setter(onMethod = @__(@Autowired))
    private static AiUtil aiUtil;
    @Setter(onMethod = @__(@Autowired))
    private ApplicationContext applicationContext;

    private static IUserService iUserService;
    private static IBizAiMessageService iBizAiMessageService;
    private static IBizAiMessageContentService iBizAiMessageContentService;

    @PostConstruct
    public void setApplicationContext() {
        log.info("开始初始化 WebSocketController 静态变量");
        try {
            iBizAiMessageService = applicationContext.getBean(IBizAiMessageService.class);
            iBizAiMessageContentService = applicationContext.getBean(IBizAiMessageContentService.class);
            iUserService = applicationContext.getBean(IUserService.class);
            aiUtil = applicationContext.getBean(AiUtil.class);
        } catch (Exception e) {
            log.error("WebSocketController 初始化失败", e);
            throw e;
        }
    }

    /**
     * UUID为key
     */
    public static final ConcurrentHashMap<String, Session> USER_MAP = new ConcurrentHashMap<>(256);
    public static final ConcurrentHashMap<String, String> TOKEN_MAP = new ConcurrentHashMap<>(256);


    private static String userKey(String userId, Session session) {
        return userId + "_" + session.getId();
    }

    public static void removeSessionsByUserId(String userId) {
        for (String key : USER_MAP.keySet()) {
            if (key.startsWith(userId + "_")) {
                remove(key);
            }
        }
    }

    private static void put(String key, Session session, String token) {
        if (ObjectUtil.isEmpty(USER_MAP.get(key))) {
            USER_MAP.put(key, session);
            TOKEN_MAP.put(key, token);
        }
    }

    private static void remove(String key) {
        USER_MAP.remove(key);
        TOKEN_MAP.remove(key);
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "token") String token, @PathParam(value = "userId") String userId) throws IOException {
        log.info("session:{}", JSONUtil.toJsonStr(session));
        Object loginIdByToken = StpUtil.getLoginIdByToken(token);
        if (loginIdByToken == null || !loginIdByToken.equals(userId)) {
            outLogin(userId);
        }
        UserVo detail = iUserService.detail(Long.valueOf(userId));
        String key = userKey(userId, session);
        if (detail == null) {
//            session.close();
            throw new CommonException(HttpStatus.HTTP_UNAUTHORIZED, "用户不存在");
        } else {
            if (ObjectUtil.isEmpty(USER_MAP.get(key))) {
                put(key, session, token);
                List<BizAiMessage> list = iBizAiMessageService.list(Wrappers.<BizAiMessage>lambdaQuery()
                        .eq(BizAiMessage::getUserId, userId)
                        .orderByDesc(BizAiMessage::getCreateTime)
                        .last(" limit 10"));
                List<Message> messageList = new ArrayList<>();
                list.forEach(item -> {
                    messageList.add(Message.builder().message(item.getTitle()).messageId(item.getId().toString()).type("2").build());
                });
                //列表
                send(session, JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.MESSAGE_INFO_LIST, messageList)));
            }
            log.info("用户:{}连接成功", detail.getName());
        }
    }

    private static void outLogin(String userId) throws IOException {
        for (String key : USER_MAP.keySet()) {
            if (key.startsWith(userId + "_")) {
                Session session1 = USER_MAP.get(key);
                String token1 = TOKEN_MAP.get(key);
                //广播退出登录
                Object loginIdByToken = StpUtil.getLoginIdByToken(token1);
                if (loginIdByToken == null || !loginIdByToken.equals(userId)) {
                    log.info("用户:{}退出登录[{}]", userId, session1.getId());
                    send(session1, JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.CAN_NOT_FIND_USER_INFO, "用户已经退出登录")));
                    remove(key);
                    if(session1.isOpen()){
                        session1.close();
                    }
                }
            }
        }
    }

    private static void outOpen(String token, Session session) throws IOException {
        //列表
        session.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.CAN_NOT_FIND_USER_INFO, "退出")));
        Object loginIdByToken = StpUtil.getLoginIdByToken(token);
        if (loginIdByToken == null) {
            throw new CommonException(HttpStatus.HTTP_UNAUTHORIZED, "token无效");
        }
        String key = userKey((String) loginIdByToken, session);
        Session keySession = USER_MAP.get(key);
        USER_MAP.remove(key);
        if(keySession.isOpen()){
            keySession.close();
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam(value = "token") String token, @PathParam(value = "userId") String userId) throws IOException {
        log.info("关闭连接");
        String key = userKey(userId, session);
        Session keySession = USER_MAP.get(key);
        remove(key);
        if(keySession.isOpen()){
            keySession.close();
        }
    }

    /**
     * 连接错误调用的方法
     */
    @OnError
    public void onError(Session session, Throwable error, @PathParam(value = "token") String token, @PathParam(value = "userId") String userId) throws IOException {
        log.error("发生错误");
        String key = userKey(userId, session);
        Session keySession = USER_MAP.get(key);
        remove(key);
        if(keySession.isOpen()){
            keySession.close();
        }
    }

    /**
     * 收到客户端消息后调用的方法，该消息由用户发过来
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String message, @PathParam(value = "token") String token, @PathParam(value = "userId") String userId) throws Exception {
        log.info("收到消息：{}", message);
        TimeInterval timer = DateUtil.timer();
        Object loginIdByToken = StpUtil.getLoginIdByToken(token);
        if (loginIdByToken == null || !loginIdByToken.equals(userId)) {
            outLogin(userId);
        }
        String key = userKey(userId, session);
        Session keySession = USER_MAP.get(key);
        SendMessage sendMessage = JSONObject.parseObject(message, SendMessage.class);
        if (sendMessage.getSendEnum().equals(SendEnum.SEND_MESSAGE_TYPE)) {
            if (ObjectUtil.isEmpty(sendMessage.getMessage()) || "1111".equals(sendMessage.getMessage())) {
                send(session, JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息不能为空！")));
                return;
            }

            if (ObjectUtil.isEmpty(sendMessage.getMessageId())) {
                BizAiMessage aiMessage = iBizAiMessageService.add(BizAiMessageParamDto.builder()
                        .title(sendMessage.getMessage())
                        .userId(Long.valueOf(userId))
                        .build());
                sendMessage.setMessageId(aiMessage.getId());
                //列表
                send(session, JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.ADD_MESSAGE_INFO_LIST, Message.builder().message(aiMessage.getTitle()).messageId(aiMessage.getId().toString()).type("2").build())));
            } else {
                BizAiMessage aiMessage = iBizAiMessageService.getById(sendMessage.getMessageId());
                if (ObjectUtil.isEmpty(aiMessage) || !aiMessage.getUserId().equals(Long.valueOf(userId))) {
                    send(session, JSONObject.toJSONString(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息发送用户异常！"))));
//                    keySession.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息发送用户异常！")));
                    return;
                }
            }
            if (ObjectUtil.isEmpty(sendMessage.getMessageId())) {
                send(session, JSONObject.toJSONString(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息发送失败！"))));
//                keySession.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息发送失败！")));
                return;
            }
            Long messageId = sendMessage.getMessageId();
            BizAiMessage bizAiMessage = iBizAiMessageService.getById(messageId);
            if (ObjectUtil.isEmpty(bizAiMessage)) {
                send(session, JSONObject.toJSONString(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不存在！"))));
//                keySession.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不存在！")));
                return;
            }
            sendMessagePush(messageId, sendMessage, timer, keySession);
        } else if (sendMessage.getSendEnum().equals(SendEnum.OPEN_MESSAGE_TYPE)) {
            int page = sendMessage.getPage();
            int size = sendMessage.getSize();
            if (ObjectUtil.isEmpty(sendMessage.getMessageId())) {
                send(session, JSONObject.toJSONString(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不能为空！"))));
//                keySession.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不能为空！")));
                return;
            }
            BizAiMessage aiMessage = iBizAiMessageService.getById(sendMessage.getMessageId());
            if (aiMessage == null) {
                send(session, JSONObject.toJSONString(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不存在！"))));
//                keySession.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.showFailMsg("消息体不存在！")));
                return;
            }
            Page<BizAiMessageContent> list = iBizAiMessageContentService.page(new Page<>(page, size), Wrappers.<BizAiMessageContent>lambdaQuery().eq(BizAiMessageContent::getMessageId, aiMessage.getId()).orderByDesc(BizAiMessageContent::getCreateTime));
            send(session, JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.MESSAGE_INFO_LOG_LIST, list)));
//            session.getBasicRemote().sendText(JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.MESSAGE_INFO_LOG_LIST, list)));
        }
    }

    private static <T> void send(Session session, String sendText) {
        if (session.isOpen()) {
            try {
                session.getBasicRemote().sendText(sendText);
            } catch (IOException e) {
                log.info("链接断开.................");
            }
        }
    }

    private static void sendMessagePush(Long messageId, SendMessage sendMessage, TimeInterval timer, Session keySession) throws IOException {
        try {
            //问
            BizAiMessageContentParamDto aiMessageContent = BizAiMessageContentParamDto.builder()
                    .messageId(messageId)
                    .content(sendMessage.getMessage())
                    .build();
            iBizAiMessageContentService.add(aiMessageContent, "1");
            ThreadUtil.execAsync(() -> {
                BizAiMessageContentParamDto responseMessage = null;
                String content = null;
                content = aiUtil.send(sendMessage.getMessage());
                //答
                responseMessage = BizAiMessageContentParamDto.builder()
                        .messageId(messageId)
                        .content(content)
                        .build();
                iBizAiMessageContentService.add(responseMessage, "2");
                log.info("相应时间：{}毫秒\t{}分钟", timer.interval(), timer.intervalMinute());
                log.info("ai问答\t问：{}\n答：{}", sendMessage.getMessage(), content);
                Message build = Message.builder().message(content).messageId(messageId.toString()).type("2").build();
                send(keySession, JSONObject.toJSONString(ServerResponseEntity.success(MsgCode.MESSAGE_SEND, build)));
            });
        } catch (Exception e) {
            send(keySession, JSONObject.toJSONString(ServerResponseEntity.showFailMsg("ai请求异常！")));
        }
    }

//    @Bean
//    public ServerEndpointExporter serverEndpointExporter() {
//        return new ServerEndpointExporter();
//    }

}
